#define CONFIGURE_VERSION	5

#define EDIT_SOURCE
#define NO_MALLOC
#define NO_SOCKETS
#define NO_OPCODES
#include "std.h"
#include "lex.h"
#include "preprocess.h"
#include "make_func.h"
#include "cc.h"
#include "hash.h"

#ifdef WIN32
#include <process.h>
#include <malloc.h>
#endif

#if defined(DEBUG) || defined(WIN32)
#define TO_DEV_NULL ""
#else
#define TO_DEV_NULL ">/dev/null 2>&1"
#endif

/* Using an include file at this point would be bad */
#ifdef PEDANTIC
char *malloc(int);
char *realloc(char *, int);
void free(char *);
#endif

char *outp;
static int buffered = 0;
static int nexpands = 0;

FILE *yyin = 0, *yyout = 0;

#define SYNTAX "edit_source [-process file] [-options] [-malloc] [-build_func_spec 'command'] [-build_efuns] [-configure]\n"

/* The files we fool with.  (Actually, there are more.  See -process).
 *
 * TODO: teach this file how to fix bugs in the source code :)
 */
#define OPTIONS_INCL      "options_incl.h"
#define PACKAGES          "packages/packages"
#define OPTIONS_H         "options.h"
#define LOCAL_OPTIONS     "local_options"
#define OPTION_DEFINES    "option_defs.c"
#define FUNC_SPEC         "func_spec.c"
#define FUNC_SPEC_CPP     "func_spec.cpp"
#define EFUN_TABLE        "efunctions.h"
#define OPC_PROF          "opc.h"
#define OPCODES           "opcodes.h"
#define EFUN_PROTO        "efun_protos.h"
#define EFUN_DEFS         "efun_defs.c"

#define PRAGMA_NOTE_CASE_START 1

int num_packages = 0;
char *packages[100];
char ppchar;

char *current_file = 0;
int current_line;

int grammar_mode = 0; /* which section we're in for .y files */
int in_c_case, cquote, pragmas, block_nest;

char yytext[MAXLINE];
static char defbuf[DEFMAX];

typedef struct incstate_t
{
	struct incstate_t *next;
	FILE *yyin;
	int line;
	char *file;
} incstate;

static incstate *inctop = 0;

#define CHAR_QUOTE 1
#define STRING_QUOTE 2

static void add_define(char *, int, char *);

#ifdef WIN32
#include <io.h>

int compile(char *command)
{
	FILE *tf = fopen("trash_me.bat","wt+");

	fprintf(tf,"%s%s\n%s",
			"@echo off\n",
			command,
			"if errorlevel == 1 goto error\n"
			"del trash_me.err >nul\n"
			"goto ok\n"
			":error\n"
			"echo ERROR > trash_me.err\n"
			":ok\n");
	fclose(tf);

	if (!system("trash_me.bat > nul")) return 1;
	if (_access("trash_me.err",0) ) return 1;
	return 0;
}
#else
int compile(char * str)
{
	return system(str);
}
#endif

#if defined(WIN32) || defined(LATTICE)
int dos_style_link(char * x, char * y)
{
	char link_cmd[100];
	sprintf(link_cmd, "copy %s %s", x, y);
	return system(link_cmd);
}
#endif

void yyerror(char * str)
{
	fprintf(stderr, "%s:%d: %s\n", current_file, current_line, str);
	exit(1);
}

void mf_fatal(char * str)
{
	yyerror(str);
}

void yywarn(char * str)
{
	/* ignore errors :)  local_options generates redefinition warnings,
	 which we don't want to see */
}

void yyerrorp(char * str)
{
	char buff[200];
	sprintf(buff, str, ppchar);
	fprintf(stderr, "%s:%d: %s\n", current_file, current_line, buff);
	exit(1);
}

static void add_input(char * p)
{
	int l = strlen(p);

	if (outp - l < defbuf)
		yyerror("Macro expansion buffer overflow.\n");
	strncpy(outp - l, p, l);
	outp -= l;
}

#define SKIPW(foo) while (isspace(*foo)) foo++;

static char *skip_comment(tmp, flag)
	char *tmp;int flag;
{
	int c;

	for (;;)
	{
		while ((c = *++tmp) != '*')
		{
			if (c == EOF
			)
				yyerror("End of file in a comment");
			if (c == '\n')
			{
				nexpands = 0;
				current_line++;
				if (!fgets(yytext, MAXLINE - 1, yyin))
					yyerror("End of file in a comment");
				if (flag && yyout)
					fputs(yytext, yyout);
				tmp = yytext - 1;
			}
		}
		do
		{
			if ((c = *++tmp) == '/')
				return tmp + 1;
			if (c == '\n')
			{
				nexpands = 0;
				current_line++;
				if (!fgets(yytext, MAXLINE - 1, yyin))
					yyerror("End of file in a comment");
				if (flag && yyout)
					fputs(yytext, yyout);
				tmp = yytext - 1;
			}
		}
		while (c == '*');
	}
}

static void refill()
{
	register char *p, *yyp;
	int c;

	if (fgets(p = yyp = defbuf + (DEFMAX >> 1), MAXLINE - 1, yyin))
	{
		while (((c = *yyp++) != '\n') && (c != EOF))
		{
			if (c == '/')
			{
				if ((c = *yyp) == '*')
				{
					yyp = skip_comment(yyp, 0);
					continue;
				}
				else if (c == '/')
					break;
			}
			*p++ = c;
		}
	}
	else
		yyerror("End of macro definition in \\");
	nexpands = 0;
	current_line++;
	*p = 0;
	return;
}

static void handle_define()
{
	char namebuf[NSIZE];
	char args[NARGS][NSIZE];
	char mtext[MLEN];
	char *end;
	register char *tmp = outp, *q;

	q = namebuf;
	end = q + NSIZE - 1;
	while (isalunum(*tmp))
	{
		if (q < end)
			*q++ = *tmp++;
		else
			yyerror("Name too long.\n");
	}
	if (q == namebuf)
		yyerror("Macro name missing.\n");
	if (*namebuf != '_' && !isalpha(*namebuf)
	)
		yyerror("Invalid macro name.\n");
	*q = 0;
	if (*tmp == '(')
	{ /* if "function macro" */
		int arg;
		int inid;
		char *ids = (char *) NULL;

		tmp++; /* skip '(' */
		SKIPW(tmp);
		if (*tmp == ')')
		{
			arg = 0;
		}
		else
		{
			for (arg = 0; arg < NARGS;)
			{
				end = (q = args[arg]) + NSIZE - 1;
				while (isalunum(*tmp) || (*tmp == '#'))
				{
					if (q < end)
						*q++ = *tmp++;
					else
						yyerror("Name too long.\n");
				}
				if (q == args[arg])
				{
					char buff[200];
					sprintf(buff,
					        "Missing argument %d in #define parameter list",
					        arg + 1);
					yyerror(buff);
				}
				arg++;
				SKIPW(tmp);
				if (*tmp == ')')
					break;
				if (*tmp++ != ',')
				{
					yyerror("Missing ',' in #define parameter list");
				}SKIPW(tmp);
			}
			if (arg == NARGS
			)
				yyerror("Too many macro arguments");
		}
		tmp++; /* skip ')' */
		end = mtext + MLEN - 2;
		for (inid = 0, q = mtext; *tmp;)
		{
			if (isalunum(*tmp))
			{
				if (!inid)
				{
					inid++;
					ids = tmp;
				}
			}
			else
			{
				if (inid)
				{
					int idlen = tmp - ids;
					int n, l;

					for (n = 0; n < arg; n++)
					{
						l = strlen(args[n]);
						if (l == idlen && strncmp(args[n], ids, l) == 0)
						{
							q -= idlen;
							*q++ = MARKS;
							*q++ = n + MARKS + 1;
							break;
						}
					}
					inid = 0;
				}
			}
			if ((*q = *tmp++) == MARKS
			)
				*++q = MARKS;
			if (q < end)
				q++;
			else
				yyerror("Macro text too long");
			if (!*tmp && tmp[-2] == '\\')
			{
				q -= 2;
				refill();
				tmp = defbuf + (DEFMAX >> 1);
			}
		}
		*--q = 0;
		add_define(namebuf, arg, mtext);
	}
	else if (isspace(*tmp) || (!*tmp && (*(tmp + 1) = '\0', *tmp = ' ')))
	{
		end = mtext + MLEN - 2;
		for (q = mtext; *tmp;)
		{
			*q = *tmp++;
			if (q < end)
				q++;
			else
				yyerror("Macro text too long");
			if (!*tmp && tmp[-2] == '\\')
			{
				q -= 2;
				refill();
				tmp = defbuf + (DEFMAX >> 1);
			}
		}
		*q = 0;
		add_define(namebuf, -1, mtext);
	}
	else
	{
		yyerror("Illegal macro symbol");
	}
	return;
}

#define SKPW while (isspace(*outp)) outp++

static int cmygetc()
{
	int c;

	for (;;)
	{
		if ((c = *outp++) == '/')
		{
			if ((c = *outp) == '*')
				outp = skip_comment(outp, 0);
			else if (c == '/')
				return -1;
			else
				return c;
		}
		else
			return c;
	}
}

/* Check if yytext is a macro and expand if it is. */
static int expand_define()
{
	defn_t *p;
	char expbuf[DEFMAX];
	char *args[NARGS];
	char buf[DEFMAX];
	char *q, *e, *b;

	if (nexpands++ > EXPANDMAX
	)
		yyerror("Too many macro expansions");
	if (!(p = lookup_define(yytext)))
		return 0;
	if (p->nargs == -1)
	{
		add_input(p->exps);
	}
	else
	{
		int c, parcnt = 0, dquote = 0, squote = 0;
		int n;

		SKPW;
		if (*outp++ != '(')
			yyerror("Missing '(' in macro call");
		SKPW;
		if ((c = *outp++) == ')')
			n = 0;
		else
		{
			q = expbuf;
			args[0] = q;
			for (n = 0; n < NARGS;)
			{
				switch (c)
				{
					case '"':
						if (!squote)
							dquote ^= 1;
						break;
					case '\'':
						if (!dquote)
							squote ^= 1;
						break;
					case '(':
						if (!squote && !dquote)
							parcnt++;
						break;
					case ')':
						if (!squote && !dquote)
							parcnt--;
						break;
					case '#':
						if (!squote && !dquote)
						{
							*q++ = c;
							if (*outp++ != '#')
								yyerror("'#' expected");
						}
						break;
					case '\\':
						if (squote || dquote)
						{
							*q++ = c;
							c = *outp++;
						}
						break;
					case '\n':
						if (squote || dquote)
							yyerror("Newline in string");
						break;
				}
				if (c == ',' && !parcnt && !dquote && !squote)
				{
					*q++ = 0;
					args[++n] = q;
				}
				else if (parcnt < 0)
				{
					*q++ = 0;
					n++;
					break;
				}
				else
				{
					if (c == EOF
					)
						yyerror("Unexpected end of file");
					if (q >= expbuf + DEFMAX - 5)
					{
						yyerror("Macro argument overflow");
					}
					else
					{
						*q++ = c;
					}
				}
				if (!squote && !dquote)
				{
					if ((c = cmygetc()) < 0)
						yyerror("End of macro in // comment");
				}
				else
					c = *outp++;
			}
			if (n == NARGS)
			{
				yyerror("Maximum macro argument count exceeded");
				return 0;
			}
		}
		if (n != p->nargs)
		{
			yyerror("Wrong number of macro arguments");
			return 0;
		}
		/* Do expansion */
		b = buf;
		e = p->exps;
		while (*e)
		{
			if (*e == '#' && *(e + 1) == '#')
				e += 2;
			if (*e == MARKS)
			{
				if (*++e == MARKS
				)
					*b++ = *e++;
				else
				{
					for (q = args[*e++ - MARKS - 1]; *q;)
					{
						*b++ = *q++;
						if (b >= buf + DEFMAX
						)
							yyerror("Macro expansion overflow");
					}
				}
			}
			else
			{
				*b++ = *e++;
				if (b >= buf + DEFMAX
				)
					yyerror("Macro expansion overflow");
			}
		}
		*b++ = 0;
		add_input(buf);
	}
	return 1;
}

static int exgetc()
{
	register char c, *yyp;

	SKPW;
	while (isalpha(c = *outp) || c == '_')
	{
		yyp = yytext;
		do
		{
			*yyp++ = c;
		}
		while (isalnum(c = *++outp) || (c == '_'));
		*yyp = '\0';
		if (!strcmp(yytext, "defined"))
		{
			/* handle the defined "function" in #/%if */
			SKPW;
			if (*outp++ != '(')
				yyerror("Missing ( after 'defined'");
			SKPW;
			yyp = yytext;
			if (isalpha(c = *outp) || c == '_')
			{
				do
				{
					*yyp++ = c;
				}
				while (isalnum(c = *++outp) || (c == '_'));
				*yyp = '\0';
			}
			else
				yyerror("Incorrect definition macro after defined(\n");
			SKPW;
			if (*outp != ')')
				yyerror("Missing ) in defined");
			if (lookup_define(yytext))
				add_input("1 ");
			else
				add_input("0 ");
		}
		else
		{
			if (!expand_define())
				add_input("0 ");
			else
				SKPW;
		}
	}
	return c;
}

static int skip_to(token, atoken)
	char *token, *atoken;
{
	char b[20], *p, *end;
	int c;
	int nest;

	for (nest = 0;;)
	{
		if (!fgets(outp = defbuf + (DEFMAX >> 1), MAXLINE - 1, yyin))
		{
			yyerror("Unexpected end of file while skipping");
		}
		current_line++;
		if ((c = *outp++) == ppchar)
		{
			while (isspace(*outp))
				outp++;
			end = b + sizeof b - 1;
			for (p = b; (c = *outp++) != '\n' && !isspace(c) && c != EOF;)
			{
				if (p < end)
					*p++ = c;
			}
			*p = 0;
			if (!strcmp(b, "if") || !strcmp(b, "ifdef") || !strcmp(b, "ifndef"))
			{
				nest++;
			}
			else if (nest > 0)
			{
				if (!strcmp(b, "endif"))
					nest--;
			}
			else
			{
				if (!strcmp(b, token))
				{
					*--outp = c;
					add_input(b);
					*--outp = ppchar;
					buffered = 1;
					return 1;
				}
				else if (atoken && !strcmp(b, atoken))
				{
					*--outp = c;
					add_input(b);
					*--outp = ppchar;
					buffered = 1;
					return 0;
				}
				else if (!strcmp(b, "elif"))
				{
					*--outp = c;
					add_input(b);
					*--outp = ppchar;
					buffered = 1;
					return !atoken;
				}
			}
		}
	}
}

#include "preprocess.c"

static int maybe_open_input_file(char * fn)
{
	if ((yyin = fopen(fn, "r")) == NULL)
	{
		return 0;
	}
	if (current_file)
		free((char *) current_file);
	current_file = (char *) malloc(strlen(fn) + 1);
	current_line = 0;
	strcpy(current_file, fn);
	return 1;
}

static void open_input_file(char * fn)
{
	if (!maybe_open_input_file(fn))
	{
		perror(fn);
		exit(-1);
	}
}

static void open_output_file(char * fn)
{
	if ((yyout = fopen(fn, "w")) == NULL)
	{
		perror(fn);
		exit(-1);
	}
}

static void close_output_file()
{
	fclose(yyout);
	yyout = 0;
}

static char *protect(char * p)
{
	static char buf[1024];
	char *bufp = buf;

	while (*p)
	{
		if (*p == '"' || *p == '\\')
			*bufp++ = '\\';
		*bufp++ = *p++;
	}
	*bufp = 0;
	return buf;
}

static void create_option_defines()
{
	defn_t *p;
	int count = 0;
	int i;

	fprintf(stderr, "Writing build options to %s ...\n", OPTION_DEFINES);
	open_output_file(OPTION_DEFINES);
	fprintf(yyout, "{\n");
	for (i = 0; i < DEFHASH; i++)
	{
		for (p = defns[i]; p; p = p->next)
			if (!(p->flags & DEF_IS_UNDEFINED))
			{
				count++;
				fprintf(yyout, "  \"__%s__\", \"%s\",\n", p->name,
				        protect(p->exps));
				if (strncmp(p->name, "PACKAGE_", 8) == 0)
				{
					int len;
					char *tmp, *t;

					len = strlen(p->name + 8);
					t = tmp = (char *) malloc(len + 1);
					strcpy(tmp, p->name + 8);
					while (*t)
					{
						if (isupper(*t))
							*t = tolower(*t);
						t++;
					}
					if (num_packages == 100)
					{
						fprintf(stderr, "Too many packages.\n");
						exit(-1);
					}
					packages[num_packages++] = tmp;
				}
			}
	}
	fprintf(yyout, "};\n\n#define NUM_OPTION_DEFS %d\n\n", count);
	close_output_file();
}

static void deltrail()
{
	register char *p;

	p = outp;
	while (*p && !isspace(*p) && *p != '\n')
	{
		p++;
	}
	*p = 0;
}

static void handle_include(char * name)
{
	char *p;
	static char buf[1024];
	FILE *f;
	incstate *is;

	if (*name != '"')
	{
		defn_t *d;

		if ((d = lookup_define(name)) && d->nargs == -1)
		{
			char *q;

			q = d->exps;
			while (isspace(*q))
				q++;
			handle_include(q);
		}
		else
		{
			yyerrorp("Missing leading \" in %cinclude");
		}
		return;
	}
	for (p = ++name; *p && *p != '"'; p++)
		;
	if (!*p)
		yyerrorp("Missing trailing \" in %cinclude");

	*p = 0;
	if ((f = fopen(name, "r")) != NULL)
	{
		is = (incstate *) malloc(
		        sizeof(incstate) /*, 61, "handle_include: 1" */);
		is->yyin = yyin;
		is->line = current_line;
		is->file = current_file;
		is->next = inctop;
		inctop = is;
		current_line = 0;
		current_file = (char *) malloc(
		        strlen(name) + 1 /*, 62, "handle_include: 2" */);
		strcpy(current_file, name);
		yyin = f;
	}
	else
	{
		sprintf(buf, "Cannot %cinclude %s", ppchar, name);
		yyerror(buf);
	}
}

static void handle_pragma(char * name)
{
	if (!strcmp(name, "auto_note_compiler_case_start"))
		pragmas |= PRAGMA_NOTE_CASE_START;
	else if (!strcmp(name, "no_auto_note_compiler_case_start"))
		pragmas &= ~PRAGMA_NOTE_CASE_START;
	else if (!strncmp(name, "ppchar:", 7) && *(name + 8))
		ppchar = *(name + 8);
	else
		yyerrorp("Unidentified %cpragma");
}

static void preprocess()
{
	register char *yyp, *yyp2;
	int c;
	int cond;

	while (buffered ?
	        (yyp = yyp2 = outp) : fgets(yyp = yyp2 = defbuf + (DEFMAX >> 1), MAXLINE-1, yyin)){
	        if (!buffered) current_line++;
	        else buffered = 0;
	        while (isspace(*yyp2)) yyp2++;
	        if ((c = *yyp2) == ppchar)
	        {
		        int quote = 0;
		        char sp_buf = 0, *oldoutp;

		        if (c == '%' && yyp2[1] == '%')
		        grammar_mode++;
		        outp = 0;
		        if (yyp != yyp2) yyerrorp("Misplaced '%c'.\n");
		        while (isspace(*++yyp2));
		        yyp++;
		        for (;;)
		        {
			        if ((c = *yyp2++) == '"') quote ^= 1;
			        else
			        {
				        if (!quote && c == '/')
				        {
					        if (*yyp2 == '*')
					        {
						        yyp2 = skip_comment(yyp2, 0);
						        continue;
					        }
					        else if (*yyp2 == '/') break;
				        }
				        if (!outp && isspace(c)) outp = yyp;
				        if (c == '\n' || c == EOF) break;
			        }
			        *yyp++ = c;
		        }

		        if (outp)
		        {
			        if (yyout) sp_buf = *(oldoutp = outp);
			        *outp++ = 0;
			        while (isspace(*outp)) outp++;
		        }
		        else outp = yyp;
		        *yyp = 0;
		        yyp = defbuf + (DEFMAX >> 1) + 1;

		        if (!strcmp("define", yyp))
		        {
			        handle_define();
		        }
		        else if (!strcmp("if", yyp))
		        {
			        cond = cond_get_exp(0);
			        if (*outp != '\n') yyerrorp("Condition too complex in %cif");
		else handle_cond(cond);
	    } else if (!strcmp("ifdef", yyp)) {
		deltrail();
		handle_cond(lookup_define(outp) != 0);
	    } else if (!strcmp("ifndef", yyp)) {
		deltrail();
		handle_cond(!lookup_define(outp));
	    } else if (!strcmp("elif", yyp)) {
		handle_elif();
	    } else if (!strcmp("else", yyp)) {
		handle_else();
	    } else if (!strcmp("endif", yyp)) {
		handle_endif();
	    } else if (!strcmp("undef", yyp)) {
		defn_t *d;
		
		deltrail();
		if ((d = lookup_definition(outp))) {
		    d->flags |= DEF_IS_UNDEFINED;
		    d->flags &= ~DEF_IS_NOT_LOCAL;
		} else {
		    add_define(outp, -1, " ");
		    d = lookup_definition(outp);
		    d->flags |= DEF_IS_UNDEFINED;
		    d->flags &= ~DEF_IS_NOT_LOCAL;
		}
	    } else if (!strcmp("echo", yyp)) {
		fprintf(stderr, "echo at line %d of %s: %s\n", current_line, current_file, outp);
	    } else if (!strcmp("include", yyp)) {
		handle_include(outp);
	    } else if (!strcmp("pragma", yyp)) {
		handle_pragma(outp);
	    } else if (yyout) {
		if (!strcmp("line", yyp)) {
		    fprintf(yyout, "#line %d \"%s\"\n", current_line,
			    current_file);
		} else {
		    if (sp_buf) *oldoutp = sp_buf;
		    if (pragmas & PRAGMA_NOTE_CASE_START) {
			if (*yyp == '%') pragmas &= ~PRAGMA_NOTE_CASE_START;
		    }
		    fprintf(yyout, "%s\n", yyp-1);
		}
	    } else {
		char buff[200];
		sprintf(buff, "Unrecognised %c directive : %s\n", ppchar, yyp);
		yyerror(buff);
	    }
	}
	else if (c == '/') {
	    if ((c = *++yyp2) == '*') {
		if (yyout) fputs(yyp, yyout);
		yyp2 = skip_comment(yyp2, 1);
	    } else if (c == '/' && !yyout) continue;
	    else if (yyout) {
		fprintf(yyout, "%s", yyp);
	    }
	}
	else if (yyout) {
	    fprintf(yyout, "%s", yyp);
	    if (pragmas & PRAGMA_NOTE_CASE_START) {
		static int line_to_print;
		
		line_to_print = 0;
		
		if (!in_c_case) {
		    while (isalunum(*yyp2)) yyp2++;
		    while (isspace(*yyp2)) yyp2++;
		    if (*yyp2 == ':') {
			in_c_case = 1;
			yyp2++;
		    }
		}
		
		if (in_c_case) {
		    while ((c = *yyp2++)) {
			switch(c) {
			  case '{':
			    {
				if (!cquote && (++block_nest == 1))
				    line_to_print = 1;
				break;
			    }
			    
			  case '}':
			    {
				if (!cquote) {
				    if (--block_nest < 0) yyerror("Too many }'s");
				}
				break;
			    }
			    
			  case '"':
                            if (!(cquote & CHAR_QUOTE)) cquote ^= STRING_QUOTE;
                            break;
			    
			  case '\'':
                            if (!(cquote & STRING_QUOTE)) cquote ^= CHAR_QUOTE;
                            break;
			    
			  case '\\':
                            if (cquote && *yyp2) yyp2++;
                            break;
			    
			  case '/':
                            if (!cquote) {
                                if ((c = *yyp2) == '*') {
                                    yyp2 = skip_comment(yyp2, 1);
                                } else if (c == '/') {
                                    *(yyp2-1) = '\n';
                                    *yyp2 = '\0';
                                }
                            }
                            break;
			    
			  case ':':
                            if (!cquote && !block_nest)
                                yyerror("Case started before ending previous case with ;");
                            break;
			    
			  case ';':
                            if (!cquote && !block_nest) in_c_case = 0;
			}
		    }
		}
		
		if (line_to_print)
		    fprintf(yyout, "#line %d \"%s\"\n", current_line + 1,current_file);

	    }
	}
    }
	if (iftop)
	{
		ifstate_t *p = iftop;

		while (iftop)
		{
			p = iftop;
			iftop = p->next;
			free((char *) p);
		}
		yyerrorp("Missing %cendif");
	}
	fclose(yyin);
	free(current_file);
	current_file = 0;
	nexpands = 0;
	if (inctop)
	{
		incstate *p = inctop;

		current_file = p->file;
		current_line = p->line;
		yyin = p->yyin;
		inctop = p->next;
		free((char *) p);
		preprocess();
	}
	else
		yyin = 0;
}

void make_efun_tables()
{
#define NUM_FILES     5
	static char* outfiles[NUM_FILES] = { EFUN_TABLE, OPC_PROF, OPCODES,
	        EFUN_PROTO, EFUN_DEFS };
	FILE *files[NUM_FILES];
	int i;

	fprintf(stderr, "Building efun tables ...\n");
	for (i = 0; i < NUM_FILES; i++)
	{
		files[i] = fopen(outfiles[i], "w");
		if (!files[i])
		{
			fprintf(stderr, "make_func: unable to open %s\n", outfiles[i]);
			exit(-1);
		}
		fprintf(files[i],
		        "/*\n\tThis file is automatically generated by make_func.\n");
		fprintf(files[i],
		        "\tdo not make any manual changes to this file.\n*/\n\n");
	}

	fprintf(files[0], "\n#include \"efun_protos.h\"\n\n");
	fprintf(files[0], "\ntypedef void (*func_t)(void));\n\n");
	fprintf(files[0], "func_t efun_table[] = {\n");

	fprintf(files[1],
	        "\ntypedef struct opc_s { char *name; int count; } opc_t;\n\n");
	fprintf(files[1], "opc_t opc_efun[] = {\n");

	fprintf(files[2], "\n/* operators */\n\n");
	for (i = 0; i < op_code; i++)
	{
		fprintf(files[2], "#define %-30s %d\n", oper_codes[i], i + 1);
	}

	fprintf(files[2], "\n/* 1 arg efuns */\n#define BASE %d\n\n", op_code + 1);
	for (i = 0; i < efun1_code; i++)
	{
		fprintf(files[0], "\tf_%s,\n", efun1_names[i]);
		fprintf(files[1], "{\"%s\", 0},\n", efun1_names[i]);
		fprintf(files[2], "#define %-30s %d\n", efun1_codes[i],
		        i + op_code + 1);
		fprintf(files[3], "void f_%s(void));\n", efun1_names[i]);
	}

	fprintf(files[2], "\n/* efuns */\n#define ONEARG_MAX %d\n\n",
	        efun1_code + op_code + 1);
	for (i = 0; i < efun_code; i++)
	{
		fprintf(files[0], "\tf_%s,\n", efun_names[i]);
		fprintf(files[1], "{\"%s\", 0},\n", efun_names[i]);
		fprintf(files[2], "#define %-30s %d\n", efun_codes[i],
		        i + op_code + efun1_code + 1);
		fprintf(files[3], "void f_%s(void));\n", efun_names[i]);
	}
	fprintf(files[0], "};\n");
	fprintf(files[1], "};\n");

	if (efun1_code + op_code >= 256)
	{
		fprintf(
		        stderr,
		        "You have way too many efuns.  Contact the MudOS developers if you really need this many.\n");
	}
	if (efun_code >= 256)
	{
		fprintf(
		        stderr,
		        "You have way too many efuns.  Contact the MudOS developers if you really need this many.\n");
	}
	fprintf(files[2], "\n/* efuns */\n#define NUM_OPCODES %d\n\n",
	        efun_code + efun1_code + op_code);

	/* Now sort the main_list */
	for (i = 0; i < num_buff; i++)
	{
		int j;
		for (j = 0; j < i; j++)
			if (strcmp(key[i], key[j]) < 0)
			{
				char *tmp;
				tmp = key[i];
				key[i] = key[j];
				key[j] = tmp;
				tmp = buf[i];
				buf[i] = buf[j];
				buf[j] = tmp;
			}
	}

	/* Now display it... */
	fprintf(files[4], "{\n");
	for (i = 0; i < num_buff; i++)
		fprintf(files[4], "%s", buf[i]);
	fprintf(files[4], "\n};\nint efun_arg_types[] = {\n");
	for (i = 0; i < last_current_type; i++)
	{
		if (arg_types[i] == 0)
			fprintf(files[4], "0,\n");
		else
			fprintf(files[4], "%s,", ctype(arg_types[i]));
	}
	fprintf(files[4], "};\n");

	for (i = 0; i < NUM_FILES; i++)
		fclose(files[i]);
}

static void handle_local_defines(int check)
{
	defn_t *p;
	int i;
	int problem = 0;

	for (i = 0; i < DEFHASH; i++)
		for (p = defns[i]; p; p = p->next)
			p->flags |= DEF_IS_NOT_LOCAL;

	/* undefine _OPTIONS_H_ so it doesn't get propagated to the mudlib
	 or interfere with copies of options.h */
	if ((p = lookup_define("_OPTIONS_H_")))
	{
		p->flags |= DEF_IS_UNDEFINED;
		p->flags &= ~DEF_IS_NOT_LOCAL;
	}
	if ((p = lookup_define("DEBUG")))
		p->flags &= ~DEF_IS_NOT_LOCAL;

	ppchar = '#';
	preprocess();

	if ((p = lookup_define("_OPTIONS_H_")))
		p->flags |= DEF_IS_UNDEFINED;

	if (!check)
		return;

	for (i = 0; i < DEFHASH; i++)
		for (p = defns[i]; p; p = p->next)
			if (p->flags & DEF_IS_NOT_LOCAL)
			{
				fprintf(stderr, "No setting for %s in '%s'.\n", p->name,
				        LOCAL_OPTIONS);
				problem = 1;
			}

	if (problem)
	{
		fprintf(
		        stderr,
		        "\
***This local_options file appears to have been written for an\n\
***earlier version of the MudOS driver.  Please lookup the new options\n\
***(mentioned above) in the options.h file, decide how you would like them\n\
***set, and add those settings to the local_options file.\n");
		exit(-1);
	}
}

static void write_options_incl(int local)
{
	open_output_file(OPTIONS_INCL);
	if (local)
	{
		fprintf(yyout, "#include \"%s\"\n", LOCAL_OPTIONS);
	}
	else
	{
		fprintf(yyout, "#include \"%s\"\n", OPTIONS_H);
	}
	close_output_file();
}

static void handle_options(int full)
{
	open_input_file(OPTIONS_H);
	ppchar = '#';
	preprocess();

	if (!full)
	{
		/* don't do any checking, just find out what is defined */
		if (maybe_open_input_file(LOCAL_OPTIONS))
			handle_local_defines(0);
		return;
	}

	if (maybe_open_input_file(LOCAL_OPTIONS))
	{
		fprintf(stdout, "Using '%s' file ...\n", LOCAL_OPTIONS);
		handle_local_defines(1);
		write_options_incl(1);
	}
	else
	{
		fprintf(
		        stderr,
		        "No \"%s\" file present.  If you create one from \"%s\",\nyou can use it when you get a new driver, and you will be warned if there are\nchanges to the real %s which you should include in your local file.\n",
		        LOCAL_OPTIONS, OPTIONS_H, OPTIONS_H);
		write_options_incl(0);
	}

	create_option_defines();
}

static void handle_build_func_spec(char * command)
{
	char buf[1024];
	int i;

	fprintf(stderr, "Building compiler files ...\n");
	sprintf(buf, "%s %s >%s", command, FUNC_SPEC, FUNC_SPEC_CPP);
	system(buf);
	for (i = 0; i < num_packages; i++)
	{
		sprintf(buf, "%s -I. packages/%s_spec.c >>%s", command, packages[i],
		        FUNC_SPEC_CPP);
		system(buf);
	}

	open_output_file(PACKAGES);
	fprintf(yyout, "SRC=");
	for (i = 0; i < num_packages; i++)
		fprintf(yyout, "%s.c ", packages[i]);
	fprintf(yyout, "\nOBJ=");
	for (i = 0; i < num_packages; i++)
		fprintf(yyout, "%s.$(O) ", packages[i]);
	fprintf(yyout, "\n");
	close_output_file();
}

static void handle_process(char * file)
{
	char buf[1024];
	int l;

	strcpy(buf, file);
	l = strlen(buf);
	if (strcmp(buf + l - 4, ".pre"))
	{
		fprintf(stderr, "Filename for -process must end in .pre\n");
		exit(-1);
	}
	*(buf + l - 4) = 0;

	fprintf(stderr, "Creating '%s' from '%s' ...\n", buf, file);

#ifdef DEBUG
	/* pass down the DEBUG define from CFLAGS */
	add_define("DEBUG", -1, " ");
#endif

	open_input_file(file);
	open_output_file(buf);
	ppchar = '%';
	preprocess();
	close_output_file();
}

static void handle_build_efuns()
{
	void yyparse();

	num_buff = op_code = efun_code = efun1_code = 0;

	open_input_file(FUNC_SPEC_CPP);
	yyparse();
	make_efun_tables();
}

static handle_applies()
{
	FILE *f = fopen("applies", "r");
	FILE *out = fopen("applies.h", "w");
	FILE *table = fopen("applies_table.c", "w");
	char buf[8192];
	char *colon;
	char *p;
	int apply_number = 0;

	fprintf(
	        out,
	        "/* autogenerated from 'applies' */\n#ifndef APPLIES_H\n#define APPLIES_H\n\nextern char *applies_table[];\n\n/* the folowing must be the first character of __INIT */\n#define APPLY___INIT_SPECIAL_CHAR\t\t'#'\n");
	fprintf(
	        table,
	        "/* autogenerated from 'applies' */\n\nchar *applies_table[] = {\n");

	while (fgets(buf, 8192, f))
	{
		buf[strlen(buf) - 1] = 0;
		if (buf[0] == '#')
			break;
		if ((colon = strchr(buf, ':')))
		{
			*colon++ = 0;
			fprintf(out, "#define APPLY_%-30s\t\"%s\"\n", buf, colon);
		}
		else
		{
			fprintf(out, "#define APPLY_%-30s\t", buf);
			p = buf;
			while (*p)
			{
				*p = tolower(*p);
				p++;
			}
			fprintf(out, "\"%s\"\n", buf);
		}
	}
	while (fgets(buf, 8192, f))
	{
		buf[strlen(buf) - 1] = 0;
		if ((colon = strchr(buf, ':')))
		{
			*colon++ = 0;
			fprintf(table, "\t\"%s\",\n", colon);
			fprintf(out, "#define APPLY_%-30s\t%i\n", buf, apply_number++);
		}
		else
		{
			fprintf(out, "#define APPLY_%-30s\t%i\n", buf, apply_number++);
			p = buf;
			while (*p)
			{
				*p = tolower(*p);
				p++;
			}
			fprintf(table, "\t\"%s\",\n", buf);
		}
	}

	fprintf(table, "};\n");
	fprintf(out, "\n#define NUM_MASTER_APPLIES\t%i\n\n#endif\n", apply_number);

	fclose(out);
	fclose(table);
	fclose(f);
}

static void handle_malloc()
{
#ifdef PEDANTIC
	int unlink(char *);
	int link(char *, char *);
#endif

	char *the_malloc = 0, *the_wrapper = 0;

	if (lookup_define("SYSMALLOC"))
		the_malloc = "sysmalloc.c";
	if (lookup_define("SMALLOC"))
		the_malloc = "smalloc.c";
	if (lookup_define("BSDMALLOC"))
		the_malloc = "bsdmalloc.c";

	if (lookup_define("WRAPPEDMALLOC"))
		the_wrapper = "wrappedmalloc.c";
	if (lookup_define("DEBUGMALLOC"))
		the_wrapper = "debugmalloc.c";

	if (!the_malloc && !the_wrapper)
	{
		fprintf(
		        stderr,
		        "Memory package and/or malloc wrapper incorrectly specified in options.h\n");
		exit(-1);
	}

	if (unlink("malloc.c") == -1 && errno != ENOENT
	)
		perror("unlink malloc.c");
	if (unlink("mallocwrapper.c") == -1 && errno != ENOENT
	)
		perror("unlink mallocwrapper.c");

	if (the_wrapper)
	{
		printf("Using memory allocation package: %s\n\t\tWrapped with: %s\n",
		        the_malloc, the_wrapper);
		if (link(the_wrapper, "mallocwrapper.c") == -1)
			perror("link mallocwrapper.c");
	}
	else
	{
		printf("Using memory allocation package: %s\n", the_malloc);
	}
	if (link(the_malloc, "malloc.c") == -1)
		perror("link malloc.c");
}

static int check_include2(char * tag, char * file, char * before, char * after)
{
	char buf[1024];
	FILE *ct;

	printf("Checking for include file <%s> ... ", file);
	ct = fopen("comptest.c", "w");
	fprintf(
	        ct,
	        "#include \"configure.h\"\n#include \"std_incl.h\"\n%s\n#include <%s>\n%s\n",
	        before, file, after);
	fclose(ct);

	sprintf(buf, "%s %s -c comptest.c " TO_DEV_NULL, COMPILER, CFLAGS);
	if (!compile(buf))
	{
		fprintf(yyout, "#define %s\n", tag);
		/* Make sure the define exists for later checks */
		fflush(yyout);
		printf("exists\n");
		return 1;
	}
	printf("does not exist or is unusable\n");
	return 0;
}

static int check_include(char * tag, char * file)
{
	char buf[1024];
	FILE *ct;

	printf("Checking for include file <%s> ... ", file);
	ct = fopen("comptest.c", "w");
	fprintf(
	        ct,
	        "#include \"configure.h\"\n#include \"std_incl.h\"\n#include \"file_incl.h\"\n#include <%s>\n",
	        file);
	fclose(ct);

	sprintf(buf, "%s %s -c comptest.c " TO_DEV_NULL, COMPILER, CFLAGS);
	if (!compile(buf))
	{
		fprintf(yyout, "#define %s\n", tag);
		/* Make sure the define exists for later checks */
		fflush(yyout);
		printf("exists\n");
		return 1;
	}
	printf("does not exist\n");
	return 0;
}

static int check_library(char * lib)
{
	char buf[1024];
	FILE *ct;

	printf("Checking for library %s ... ", lib);
	ct = fopen("comptest.c", "w");
	fprintf(ct, "int main() { exit(0); }\n");
	fclose(ct);

	sprintf(buf, "%s %s comptest.c %s" TO_DEV_NULL, COMPILER, CFLAGS, lib);
	if (!compile(buf))
	{
		fprintf(yyout, " %s", lib);
		printf("exists\n");
		return 1;
	}
	printf("does not exist\n");
	return 0;
}

#if 0 /* not used any more */
static int check_ret_type(char * tag, char * pre, char * type, char * func)
{
	char buf[1024];
	FILE *ct;

	printf("Checking return type of %s() ...", func);
	ct = fopen("comptest.c", "w");
	fprintf(ct, "%s\n\n%s%s();\n", pre, type, func);
	fclose(ct);

	sprintf(buf, "%s %s -c comptest.c >/dev/null 2>&1", COMPILER, CFLAGS);
	if (!system(buf))
	{
		fprintf(yyout, "#define %s\n", tag);
		printf("returns %s\n", type);
		return 1;
	}
	printf("does not return %s\n", type);
	return 0;
}
#endif

/* This should check a.out existence, not exit value */
static int check_prog(char * tag, char * pre, char * code, int andrun)
{
	char buf[1024];
	FILE *ct;

	ct = fopen("comptest.c", "w");
	fprintf(
	        ct,
	        "#include \"configure.h\"\n#include \"std_incl.h\"\n%s\n\nint main() {%s}\n",
	        (pre ? pre : ""), code);
	fclose(ct);

	sprintf(buf, "%s %s comptest.c -o comptest" TO_DEV_NULL, COMPILER, CFLAGS);
	if (!compile(buf) && (!andrun || !system("./comptest")))
	{
		if (tag)
		{
			fprintf(yyout, "#define %s\n", tag);
			fflush(yyout);
		}
		return 1;
	}

	return 0;
}

static int check_code(char * pre, char * code)
{
	char buf[1024];
	FILE *ct;
	int rc;

	ct = fopen("comptest.c", "w");
	fprintf(
	        ct,
	        "#include \"configure.h\"\n#include \"std_incl.h\"\n%s\n\nint main() {%s}\n",
	        (pre ? pre : ""), code);
	fclose(ct);

	sprintf(buf, "%s %s comptest.c -o comptest" TO_DEV_NULL, COMPILER, CFLAGS);
	if (compile(buf) || (rc = system("./comptest")) == 127 || rc == -1)
	{
		return -1;
	}
	return rc;
}

static void check_linux_libc()
{
	char buf[1024];
	FILE *ct;

	ct = fopen("comptest.c", "w");
	fprintf(ct, "int main() { }\n");
	fclose(ct);

	sprintf(buf, "%s -g comptest.c -o comptest >/dev/null 2>&1", COMPILER);
	if (system(buf))
	{
		fprintf(stderr,
		        "   libg.a/so installed wrong, trying workaround ...\n");
		sprintf(buf, "%s -g comptest.c -lc -o comptest >/dev/null 2>&1",
		        COMPILER);
		if (system(buf))
		{
			fprintf(stderr, "*** FAILED.\n");
			exit(-1);
		}
		fprintf(yyout, " -lc");
	}
}

static char *memmove_prog =
        "\
char buf[80];\n\
strcpy(buf,\"0123456789ABCDEF\");\n\
memmove(&buf[1],&buf[4],13);\n\
if(strcmp(buf,\"0456789ABCDEF\")) exit(-1);\n\
memmove(&buf[8],&buf[6],9);\n\
if(strcmp(buf,\"0456789A9ABCDEF\")) exit(-1);\n\
return 0;\n";

static int check_memmove(char * tag, char * str)
{
	return check_prog(tag, str, memmove_prog, 1);
}

static void find_memmove()
{
	printf("Checking for memmove() ...");
	if (check_memmove(0, ""))
	{
		printf(" exists\n");
		return;
	}
	if (check_memmove("USE_BCOPY", "#define memmove(a,b,c) bcopy(b,a,c)"))
	{
		printf(" simulating via bcopy()\n");
		return;
	}
	printf(" missing; using MudOS's version\n");
	fprintf(yyout, "#define MEMMOVE_MISSING\n");
}

static void verbose_check_prog(char * msg, char * def, char * pre, char * prog,
        int andrun)
{
	printf("%s ...", msg);
	if (check_prog(def, pre, prog, andrun))
		printf(" exists\n");
	else
		printf(" does not exist\n");
}

static int check_configure_version()
{
	char buf[1024];
	FILE *ct;

	ct = fopen("comptest.c", "w");
	fprintf(
	        ct,
	        "#include \"configure.h\"\n\n#if CONFIGURE_VERSION < %i\nthrash and die\n#endif\n\nint main() { }\n",
	        CONFIGURE_VERSION);
	fclose(ct);

	sprintf(buf, "%s %s comptest.c -o comptest " TO_DEV_NULL, COMPILER, CFLAGS);
	return !compile(buf);
}

static void handle_configure()
{
	if (check_configure_version())
		return;

	open_output_file("configure.h");

#ifndef WIN32
	check_include("INCL_STDLIB_H", "stdlib.h");
	check_include("INCL_UNISTD_H", "unistd.h");
	if (check_include("INCL_TIME_H", "time.h"))
	{
		if (!check_prog(0, "#include <time.h>", "tzset();", 0))
		{
			if (check_prog(0, 0, "void tzset(); tzset();", 0))
				fprintf(yyout, "#define PROTO_TZSET\n#define USE_TZSET\n");
		}
		else
			fprintf(yyout, "#define USE_TZSET\n");
	}
	else
	{
		if (check_prog(0, 0, "void tzset(); tzset();", 0))
			fprintf(yyout, "#define PROTO_TZSET\n#define USE_TZSET\n");
	}
	check_include("INCL_SYS_TIMES_H", "sys/times.h");
	check_include("INCL_FCNTL_H", "fcntl.h");
	check_include("INCL_SYS_TIME_H", "sys/time.h");
	check_include("INCL_DOS_H", "dos.h");
	check_include("INCL_USCLKC_H", "usclkc.h");
	check_include("INCL_LIMITS_H", "limits.h");
	check_include("INCL_LOCALE_H", "locale.h");
	if (!check_prog(0, 0, "int x = USHRT_MAX;", 0))
	{
		if (!check_prog(0, 0, "int x = MAXSHORT;", 0))
			check_include("INCL_VALUES_H", "values.h");
		fprintf(yyout, "#define USHRT_MAX  (MAXSHORT)\n");
	}

	check_include("INCL_NETINET_IN_H", "netinet/in.h");
	check_include("INCL_ARPA_INET_H", "arpa/inet.h");

	check_include("INCL_SYS_IOCTL_H", "sys/ioctl.h");
	check_include("INCL_SYS_SOCKET_H", "sys/socket.h");
	check_include("INCL_NETDB_H", "netdb.h");
	/* TELOPT_NAWS is missing from <arpa/telnet.h> on some systems */
	check_include2("INCL_ARPA_TELNET_H", "arpa/telnet.h", "",
	        "int i=TELOPT_NAWS;");
	check_include("INCL_SYS_SEMA_H", "sys/sema.h");
	check_include("INCL_SYS_SOCKETVAR_H", "sys/socketvar.h");
	check_include("INCL_SOCKET_H", "socket.h");
	check_include("INCL_RESOLVE_H", "resolve.h");

	check_include("INCL_SYS_STAT_H", "sys/stat.h");

	/* sys/dir.h is BSD, dirent is sys V.  Try to do it the BSD way first. */
	/* If that fails, fall back to sys V */
	if (check_prog("BSD_READDIR", "#include <sys/dir.h>",
	        "struct direct *d; d->d_namlen;", 0))
	{
		check_include("INCL_SYS_DIR_H", "sys/dir.h");
	}
	else
	{
		/* could be either of these */
		check_include("INCL_DIRENT_H", "dirent.h");
		check_include("INCL_SYS_DIRENT_H", "sys/dirent.h");
		fprintf(yyout, "#define USE_STRUCT_DIRENT\n");
	}

	check_include("INCL_SYS_FILIO_H", "sys/filio.h");
	check_include("INCL_SYS_SOCKIO_H", "sys/sockio.h");
	check_include("INCL_SYS_MKDEV_H", "sys/mkdev.h");
	check_include("INCL_SYS_RESOURCE_H", "sys/resource.h");
	check_include("INCL_SYS_RUSAGE_H", "sys/rusage.h");
	check_include("INCL_SYS_WAIT_H", "sys/wait.h");
	check_include("INCL_SYS_CRYPT_H", "sys/crypt.h");
	check_include("INCL_CRYPT_H", "crypt.h");
	check_include("INCL_MALLOC_H", "my_malloc.h");

	/* for NeXT */
	if (!check_include("INCL_MACH_MACH_H", "mach/mach.h"))
		check_include("INCL_MACH_H", "mach.h");

	/* figure out what we need to do to get major()/minor() */
	check_include("INCL_SYS_SYSMACROS_H", "sys/sysmacros.h");
	check_include("INCL_STDARG_H", "stdarg.h");

#ifdef DEBUG
	/* includes just to shut up gcc's warnings on some systems */
	check_include("INCL_BSTRING_H", "bstring.h");
#endif

	/* Runtime loading support */
	if (check_include("INCL_DLFCN_H", "dlfcn.h"))
	{
		if (!check_prog(0, "#include <dlfcn.h>", "int x = RTLD_LAZY;", 0))
			fprintf(yyout, "#define RTLD_LAZY      1\n");
	}
	else
	{
		if (!check_prog(0, 0, "int x = RTLD_LAZY;", 0))
			fprintf(yyout, "#define RTLD_LAZY     1\n");
	}

	/* SunOS is missing definition for INADDR_NONE */
	printf("Checking for missing INADDR_NONE ... ");
	if (!check_prog(0, "#include <netinet/in.h>", "int x = INADDR_NONE;", 0))
	{
		printf("missing\n");
		fprintf(yyout, "#define INADDR_NONE (unsigned int)0xffffffff\n");
	}
	else
		printf("ok\n");

	printf("Checking for random number generator ...");
	if (check_prog("DRAND48", 0, "srand48(0);", 0))
	{
		printf(" using drand48()\n");
	}
	else if (check_prog("RAND", 0, "srand(0);", 0))
	{
		printf(" using rand()\n");
	}
	else if (check_prog("RANDOM", 0, "srandom(0);", 0))
	{
		printf("using random()\n");
	}
	else
	{
		printf("WARNING: did not find a random number generator\n");
		exit(-1);
	}

	if (check_prog("USE_BSD_SIGNALS", 0, "SIGCHLD; wait3(0, 0, 0);", 0))
	{
		printf("Using BSD signals.\n");
	}
	else
	{
		printf("Using System V signals.\n");
	}

	printf("Checking if signal() returns SIG_ERR on error ...");
	if (check_prog("SIGNAL_ERROR SIG_ERR", 0, "if (signal(0, 0) == SIG_ERR) ;",
	        0))
	{
		printf(" yes\n");
	}
	else
	{
		fprintf(yyout, "#define SIGNAL_ERROR BADSIG\n");
		printf(" no\n");
	}

	printf("Checking for inline ...");
	if (!check_prog("INLINE inline", "inline void foo() { }", "foo();", 0))
	{
		printf(" __inline ...");
		if (!check_prog("INLINE __inline", "__inline void foo() {}", "foo();",
		        0))
		{
			fprintf(yyout, "#define INLINE\n");
		}
	}
	printf(" const ...\n");
	if (!check_prog("CONST const", "int foo(const int *, const int *);", "", 0))
		fprintf(yyout, "#define CONST\n");

	verbose_check_prog("Checking for ualarm()", "HAS_UALARM", "",
	        "ualarm(0, 0);", 0);
	verbose_check_prog("Checking for strerror()", "HAS_STRERROR", "",
	        "strerror(12);", 0);
	verbose_check_prog("Checking for POSIX getcwd()", "HAS_GETCWD", "",
	        "getcwd(\"\", 1000);", 0);
	verbose_check_prog("Checking for getrusage()", "RUSAGE", "",
	        "getrusage(0, 0);", 0);
	verbose_check_prog("Checking for times()", "TIMES", "", "times(0);", 0);
	verbose_check_prog("Checking for gettimeofday()", "HAS_GETTIMEOFDAY", "",
	        "gettimeofday(0, 0);", 0);
	verbose_check_prog("Checking for fchmod()", "HAS_FCHMOD", "",
	        "fchmod(0, 0);", 0);

	printf("Checking for big or little endian ... ");
	if (!check_code(
	        "char num[] = { 0x11, 0x22, 0x33, 0x44 }; int *foo = (int *)num;",
	        "return (*foo == 0x44332211);"))
	{
		printf("big\n");
		fprintf(yyout, "#define BIGENDIAN 1\n");
		fflush(yyout);
	}
	else
		printf("little\n");

	find_memmove();
#endif

	fprintf(yyout, "#define SIZEOF_INT %i\n", sizeof(int));
	fprintf(yyout, "#define SIZEOF_PTR %i\n", sizeof(char *));
	fprintf(yyout, "#define SIZEOF_SHORT %i\n", sizeof(short));
	fprintf(yyout, "#define SIZEOF_FLOAT %i\n", sizeof(float));

	if (sizeof(unsigned long) == 4)
		fprintf(yyout, "#define UINT32 unsigned long\n");
	else if (sizeof(unsigned int) == 4)
		fprintf(yyout, "#define UINT32 unsigned int\n");
	else
	{
		printf("WARNING: could not find a 32 bit integral type.\n");
		exit(-1);
	}

	/* PACKAGE_DB stuff */
	if (lookup_define("PACKAGE_DB"))
	{
		/* -I would be nicer for added include paths, but we don't have an easy way to
		 * set -I paths right now 
		 */
		if (lookup_define("USE_MSQL"))
		{
			if (!(check_include("INCL_LOCAL_MSQL_H",
			        "/usr/local/include/msql.h")
			        || check_include("INCL_LOCAL_MSQL_MSQL_H",
			                "/usr/local/msql/include/msql.h")
			        || check_include("INCL_LOCAL_MINERVA_MSQL_H",
			                "/usr/local/Minerva/include/msql.h")
			        || check_include("INCL_LIB_HUGHES_MSQL_H",
			                "/usr/lib/Hughes/include/msql.h")))
			{
				fprintf(
				        stderr,
				        "Cannot find msql.h, compilation is going to fail miserably.\n");
			}
		}
		if (lookup_define("USE_MYSQL"))
		{
			if (!(check_include("INCL_LOCAL_MYSQL_H",
			        "/usr/local/include/mysql.h")
			        || check_include("INCL_LOCAL_INCLUDE_MYSQL_MYSQL_H",
			                "/usr/local/include/mysql/mysql.h")
			        || check_include("INCL_LOCAL_MYSQL_MYSQL_H",
			                "/usr/local/mysql/include/mysql.h")
			        || check_include("INCL_MYSQL_MYSQL_H",
			                "/usr/include/mysql/mysql.h")))
			{
				fprintf(
				        stderr,
				        "Cannot find mysql.h, compilation is going to fail miserably.\n");
			}
		}
	}

	fprintf(yyout, "#define CONFIGURE_VERSION	%i\n\n", CONFIGURE_VERSION);

	close_output_file();

#ifdef WIN32
	system("copy windows\\configure.h tmp.config.h");
	system("type configure.h >> tmp.config.h");
	system("del configure.h");
	system("rename tmp.config.h configure.h");
#else

	open_output_file("system_libs");
	check_library("-lresolv");
	check_library("-lbsd");
	check_library("-lBSD");
	check_library("-ly");

	/* don't add -lcrypt if crypt() is in libc.a */
	if (!check_prog(0, "#include \"lint.h\"",
	        "char *x = crypt(\"foo\", \"bar\");", 0))
		check_library("-lcrypt");
	/* don't add -lmalloc if malloc() works */
	if (!check_prog(0, "", "char *x = malloc(100);", 0))
		check_library("-lmalloc");

	if (!check_prog(0, "", "void *x = dlopen(0, 0);", 0))
		check_library("-ldl");

	check_library("-lsocket");
	check_library("-linet");
	check_library("-lnsl");
	check_library("-lnsl_s");
	check_library("-lseq");
	check_library("-lm");

	fprintf(stderr, "Checking for flaky Linux systems ...\n");
	check_linux_libc();

	/* PACKAGE_DB stuff */
	if (lookup_define("PACKAGE_DB") && lookup_define("USE_MSQL"))
	{
		if (!(check_library("-lmsql")
		        || check_library("-L/usr/local/lib -lmsql")
		        || check_library("-L/usr/local/msql/lib -lmsql")
		        || check_library("-L/usr/local/Minerva/lib -lmsql")
		        || check_library("-L/usr/lib/Hughes/lib -lmsql")))
		{
			fprintf(
			        stderr,
			        "Cannot find libmsql.a, compilation is going to fail miserably\n");
		}
	}
	if (lookup_define("PACKAGE_DB") && lookup_define("USE_MYSQL"))
	{
		if (!(check_library("-lmysqlclient")
		        || check_library("-L/usr/local/lib -lmysqlclient")
		        || check_library("-L/usr/local/lib/mysql -lmysqlclient")
		        || check_library("-L/usr/local/mysql/lib -lmysqlclient")))
		{
			fprintf(
			        stderr,
			        "Cannot find libmysqlclient.a, compilation is going to fail miserably\n");
		}
	}

	fprintf(yyout, "\n\n");
	close_output_file();
#endif
}

int main(int argc, char ** argv)
{
	int idx = 1;

	while (idx < argc)
	{
		if (argv[idx][0] != '-')
		{
			fprintf(stderr, SYNTAX);
			exit(-1);
		}
		if (strcmp(argv[idx], "-configure") == 0)
		{
			handle_options(0);
			handle_configure();
		}
		else if (strcmp(argv[idx], "-process") == 0)
		{
			handle_process(argv[++idx]);
		}
		else if (strcmp(argv[idx], "-options") == 0)
		{
			handle_options(1);
		}
		else if (strcmp(argv[idx], "-malloc") == 0)
		{
			handle_malloc();
		}
		else if (strcmp(argv[idx], "-build_applies") == 0)
		{
			handle_applies();
		}
		else if (strcmp(argv[idx], "-build_func_spec") == 0)
		{
			handle_build_func_spec(argv[++idx]);
		}
		else if (strcmp(argv[idx], "-build_efuns") == 0)
		{
			handle_build_efuns();
		}
		else
		{
			fprintf(stderr, "Unrecognized flag %s\n", argv[idx]);
			exit(-1);
		}
		idx++;
	}
	printf("\n");
	return 0;
}
